//
// Copyright (C) 2006 Andras Varga, Levente Meszaros
// Based on the Mobility Framework's SnrEval by Marc Loebbers
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//

#include <fstream>
#include <sstream>
#include <string>
#include "BerParseFile.h"
#include <omnetpp.h>
#include <algorithm>

void BerParseFile::clearBerTable()
{
    while (!berTable.empty())
    {
        BerList *berList = &berTable.back();
        while (!berList->empty())
        {
            delete berList->back();
            berList->pop_back();
        }
        berTable.pop_back();
    }
    fileBer = false;
}

void BerParseFile::setPhyOpMode(char p)
{
    clearBerTable();
    phyOpMode = p;
    if (phyOpMode=='b')
        berTable.resize(4);
    else
        berTable.resize(8);
}



int BerParseFile::getTablePosition (double speed)
{
    if (phyOpMode=='b')
    {
        if (speed<1)
            return 0;
        else if (speed<=2&& speed<5)
            return 1;
        else if (speed<=5&& speed<11)
            return 2;
        else
            return 3;
    }
    else
    {
        if (speed<9)
            return 0;
        else if (speed<=9&& speed<12)
            return 1;
        else if (speed<=12&& speed<18)
            return 2;
        else if (speed<=18&& speed<24)
            return 3;
        else if (speed<=24&& speed<36)
            return 4;
        else if (speed<=36&& speed<48)
            return 5;
        else if (speed<=48&& speed<54)
            return 6;
        else
            return 7;
    }
}

double BerParseFile::getPer(double speed, double tsnr, int tlen)
{
    BerList *berlist;
    berlist = & berTable[getTablePosition(speed)];
    LongBer * pre;
    LongBer * pos;
    unsigned int j;
    for (j=0; j<berlist->size(); j++)
    {
        pos = *(berlist->begin()+j);
        if (pos->longpkt >=tlen)
        {
            break;
        }
    }
    if (j==0)
        pre = NULL;
    else
    {
        if (j==berlist->size())
            pre = *(berlist->begin()+j-2);
        else
            pre = *(berlist->begin()+j-1);
    }
    SnrBer snrdata1;
    SnrBer snrdata2;
    SnrBer snrdata3;
    SnrBer snrdata4;
    snrdata3.snr=-1;
    snrdata3.ber=-1;
    snrdata4.snr=-1;
    snrdata4.ber=-1;

    if (tsnr>pos->snrlist[pos->snrlist.size()-1].snr)
    {
        snrdata1 = pos->snrlist[pos->snrlist.size()-1];
        snrdata2 = pos->snrlist[pos->snrlist.size()-1];
    }
    else
    {
        for (j=0; j<pos->snrlist.size(); j++)
        {
            snrdata1 = pos->snrlist[j];
            if (tsnr<=snrdata1.snr)
                break;
        }
        if (j==0)
        {
            snrdata2.snr=-1;
            snrdata2.ber=-1;
        }
        else
        {
            if (j==pos->snrlist.size())
                snrdata2 = pos->snrlist[j-2];
            else
                snrdata2 = pos->snrlist[j-1];
        }
    }

    if (pre==NULL)
        pre = pos;
    if (tsnr>pre->snrlist[pre->snrlist.size()-1].snr)
    {
        snrdata3 = pre->snrlist[pre->snrlist.size()-1];
        snrdata4 = pre->snrlist[pre->snrlist.size()-1];
    }
    else
    {
        for (j=0; j<pre->snrlist.size(); j++)
        {
            snrdata3 = pre->snrlist[j];
            if (tsnr<=snrdata3.snr)
                break;
        }
        if (j!=0)
        {
            if (j==pre->snrlist.size())
                snrdata4 = pre->snrlist[j-2];
            else
                snrdata4 = pre->snrlist[j-1];
        }

    }
    if (snrdata2.snr==-1)
    {
        snrdata2.snr=snrdata1.snr;
        snrdata2.ber=snrdata1.ber;
    }
    if (snrdata4.snr==-1)
    {
        snrdata4.snr=snrdata3.snr;
        snrdata4.ber=snrdata3.ber;
    }
    double per1,per2,per;
    per1 = snrdata1.ber;
    per2 = snrdata3.ber;

    if (tsnr<= snrdata1.snr)
    {
        if (snrdata2.snr!=snrdata1.snr)
            per1 = snrdata1.ber +  (snrdata2.ber-snrdata1.ber)/(snrdata2.snr-snrdata1.snr)*(tsnr- snrdata1.snr);
    }
    if (tsnr<= snrdata3.snr)
    {
        if (snrdata3.snr!=snrdata4.snr)
            per2 = snrdata3.ber +  (snrdata4.ber-snrdata3.ber)/(snrdata4.snr-snrdata3.snr)*(tsnr- snrdata3.snr);
    }
    if (per1!=-1 && per2!=-1)
    {
        if (pos->longpkt != pre->longpkt)
            per = per2 +  (per1- per2)/(pos->longpkt - pre->longpkt)*(tlen-pre->longpkt);
        else
            per = per2;
    }
    else
    {
        if (per1!=-1)
        {
            per = per1;
        }
        else
        {
            if (per2!=-1)
            {
                per = per2;
            }
            else
            {
                EV << "No PER available";
            }
        }
    }
    return per;
}


void BerParseFile::parseFile(const char *filename)
{
    std::ifstream in(filename, std::ios::in);
    if (in.fail())
        opp_error("Cannot open file '%s'",filename);
    std::string line;
    std::string subline;

    while (std::getline(in, line))
    {
        // '#' line
        std::string::size_type found=line.find('#');
        if (found == 0)
            continue;
        if (found != std::string::npos)
            subline = line;
        else
            subline = line.substr(0,found);
        found=subline.find("$self");
        if (found == std::string::npos)
            continue;
        // Node Id
        found = subline.find("add");
        if (found== std::string::npos)
            continue;
        // Initial position

        std::string::size_type pos1 = subline.find("Mode");
        std::string::size_type pos2 = subline.find("Mb");
        BerList *berlist;
        std::string  substr = subline.substr(pos1+4,pos2-(pos1+4));
//                int speed = std::atof (subline.substr(pos1+4,pos2).c_str());

        int position=-1;
        if (phyOpMode=='b')
        {
            if (!strcmp(substr.c_str(),"1"))
                position =0;
            else if (!strcmp(substr.c_str(),"2"))
                position =1;
            else if (!strcmp(substr.c_str(),"5_5"))
                position =2;
            else if (!strcmp(substr.c_str(),"11"))
                position =3;
            else
                continue;
        }
        else
        {
            if (!strcmp(substr.c_str(),"6"))
                position =0;
            else if (!strcmp(substr.c_str(),"9"))
                position =1;
            else if (!strcmp(substr.c_str(),"12"))
                position =2;
            else if (!strcmp(substr.c_str(),"18"))
                position =3;
            else if (!strcmp(substr.c_str(),"24"))
                position =4;
            else if (!strcmp(substr.c_str(),"36"))
                position =5;
            else if (!strcmp(substr.c_str(),"48"))
                position =6;
            else if (!strcmp(substr.c_str(),"54"))
                position =7;
            else
                continue;
        }

        if (position<0)
            opp_error("ber parse file error");

        berlist = &berTable[position];

        std::string parameters = subline.substr (pos2+3,std::string::npos);
        std::stringstream linestream(parameters);
        int pkSize;
        double snr;
        double ber;
        linestream >> pkSize;
        linestream >> snr;
        linestream >> ber;
        snr =  dB2fraction(snr);
        if (pkSize<128)
            pkSize=128;
        else if (128<=pkSize && pkSize<256)
            pkSize=128;
        else if (256<=pkSize&& pkSize<512)
            pkSize=256;
        else if (512<=pkSize&& pkSize<1024)
            pkSize=512;
        else if (1024<=pkSize && pkSize<1500)
            pkSize=1024;
        else
            pkSize=1500;
        if (berlist->size()==0)
        {
            LongBer * l = new LongBer;
            l->longpkt = pkSize;
            berlist->push_back(l);
        }
        LongBer * l;
        bool inList = false;
        for (unsigned int j=0; j<berlist->size(); j++)
        {
            l = *(berlist->begin()+j);
            if (l->longpkt==pkSize)
            {
                inList=true;
                break;
            }
        }
        if (!inList)
        {
            l = new LongBer;
            l->longpkt = pkSize;

            unsigned int position = 0;
            for (position =0; position<berlist->size(); position++)
            {
                LongBer *aux = *(berlist->begin()+position);

                if (l->longpkt < aux->longpkt)
                    break;
            }
            if (position==berlist->size())
                berlist->push_back(l);
            else
                berlist->insert(berlist->begin()+position,l);
        }
        SnrBer snrdata;
        snrdata.snr=snr;
        snrdata.ber=ber;
        l->snrlist.push_back(snrdata);
        std::sort (l->snrlist.begin (),l->snrlist.end (), std::less<SnrBer> ());
    }
    in.close();

    // exist data?
    if (phyOpMode=='b')
    {
        for (int i = 0; i<4; i++)
            if (berTable[i].size()==0)
                opp_error("Error in ber class B file, speed not present");
    }
    else
    {
        for (int i = 0; i<8; i++)
            if (berTable[i].size()==0)
                opp_error("Error in ber class A/G file, speed not present");
    }
}

BerParseFile::~BerParseFile()
{
    clearBerTable();
}


